/*
 * RemoteDesktopCanvas.java
 *
 * Created on July 25, 2007, 2:40 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package org.mobileandembedded.idesktop;

import java.io.IOException;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;

import com.mar.meclient.bt.BTMidlet;

/**
 *
 * @author helio
 */
public class RemoteDesktopCanvas extends Canvas implements CommandListener {

    private Image desktopImage;
    private Thread updaterThread;
    private Thread desktopImageUpdaterThread;
    private int mouseStep = 4;
    private int mouseX = getWidth() / 2;
    private int mouseY = getHeight() / 2;
    private int zoom = 1;
    private int viewportX = 0;
    private int viewportY = 0;
    private int viewportWidth = getWidth();
    private int viewportHeight = getHeight();
    private boolean padUp = false;
    private boolean padDown = false;
    private boolean padLeft = false;
    private boolean padRight = false;
    private boolean mouseLeftButtonPressed = false;
    private boolean mouseRightButtonPressed = false;
    private int refreshInterval = 0;
    private boolean desktopImageUpdated = false;
    private Command disconnectCommand = new Command("Disconnect", Command.EXIT, 0);
    private ClientSideHandler clientSideHandler;

    /**
     * Creates a new instance of RemoteDesktopCanvas
     *
     * @param handler
     * @param iDesktopClient
     * @throws java.io.IOException
     */
    public RemoteDesktopCanvas(ClientSideHandler handler) throws IOException {
        this.clientSideHandler = handler;
        addCommand(disconnectCommand);
        setCommandListener(this);
        startUpdaterThread();
        startDesktopImageUpdaterThread();

//        clientSideHandler.sendDeviceConfigurationMessage();
//        clientSideHandler.sendRequestScreenshotMessage();
    }

    /**
     *
     */
    private void startDesktopImageUpdaterThread() {
        desktopImageUpdaterThread = new Thread(new Runnable() {

            public void run() {
                try {
                    while (true) {
                        if (getRefreshInterval() > 0) {
                            Thread.sleep(getRefreshInterval());

                            if (desktopImageUpdated) {
                                desktopImageUpdated = false;
                                clientSideHandler.sendRequestScreenshotMessage();
                            }
                        }

                        Thread.yield();
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        });

        desktopImageUpdaterThread.start();
    }

    /**
     *
     */
    private void startUpdaterThread() {
        updaterThread = new Thread(new Runnable() {

            public void run() {
                try {
                    while (true) {
                        /* update things */
                        updateMousePosition();
                        repaint();

                        /* wait a little */
                        Thread.sleep(50);
                        Thread.yield();
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        });

        updaterThread.start();
    }

    /**
     *
     */
    private void updateMousePosition() {
        /* up-down */
        if (padUp) {
            mouseY -= mouseStep;
        } else if (padDown) {
            mouseY += mouseStep;
        }

        /* left-right */
        if (padRight) {
            mouseX += mouseStep;
        } else if (padLeft) {
            mouseX -= mouseStep;
        }

        /* up-down boundaries */
        if (mouseX < 0) {
            mouseX = 0;
        } else if (mouseX > getWidth()) {
            mouseX = getWidth();
        }

        /* left-right boundaries */
        if (mouseY < 0) {
            mouseY = 0;
        } else if (mouseY > getHeight()) {
            mouseY = getHeight();
        }
    }

    /**
     *
     * @param keyCode
     */
    private void updateZoom(int keyCode) throws IOException {
        if (keyCode == 42 || keyCode == 35) {
            int newViewportWidth = viewportWidth;
            int newViewportHeight = viewportHeight;

            /* updates the zoom */
            if (keyCode == 35) {
                zoom--;

                if (zoom < 1) {
                    zoom = 1;
                } else {
                    newViewportWidth /= 2;
                    newViewportHeight /= 2;
                }
            } else if (keyCode == 42) {
                zoom++;

                if (zoom > 5) {
                    zoom = 5;
                } else {
                    newViewportWidth *= 2;
                    newViewportHeight *= 2;
                }
            }

            /* find new values for the viewport */

            if (viewportWidth > newViewportWidth) {
                viewportX += (newViewportWidth / 2);
                viewportY += (newViewportHeight / 2);
            } else if (viewportWidth < newViewportWidth) {
                viewportX -= (viewportWidth / 2);
                viewportY -= (viewportHeight / 2);
            }

            viewportWidth = newViewportWidth;
            viewportHeight = newViewportHeight;
        }
    }

    /**
     *
     * @param keyCode
     */
    private void updateViewportPosition(int keyCode) throws IOException {
        if (keyCode == 50 || keyCode == 56 || keyCode == 52 || keyCode == 54) {
            /* update values */
            if (keyCode == 50) {
                viewportY -= (viewportHeight / 2);
            }
            if (keyCode == 56) {
                viewportY += (viewportHeight / 2);
            }
            if (keyCode == 52) {
                viewportX -= (viewportWidth / 2);
            }
            if (keyCode == 54) {
                viewportX += (viewportWidth / 2);
            }
        }
    }

    /**
     *
     * @param keyCode
     */
    private void processMouseClicks(int keyCode) throws IOException {
        if (keyCode == 49 || keyCode == 51 || keyCode == (- 5)) {
            /* update values */
            if (keyCode == 49 || keyCode == (- 5)) {
                try {
                    clientSideHandler.sendMouseLeftClickMessage();
                } catch (IOException ex) {
                    ex.printStackTrace();
                    BTMidlet.getInstance().showAlert(ex);
                }
            }
            if (keyCode == 51) {
                try {
                    clientSideHandler.sendMouseRightClickMessage();
                } catch (IOException ex) {
                    ex.printStackTrace();
                    BTMidlet.getInstance().showAlert(ex);
                }
            }
        }
    }

    /**
     *
     * @param keyCode
     */
    private void processMousePressesReleases(int keyCode) {
        if (keyCode == 55 || keyCode == 57) {
            try {
                if (keyCode == 55 && !mouseLeftButtonPressed) {
                    clientSideHandler.sendMouseLeftPressMessage();
                    mouseLeftButtonPressed = true;
                } else if (keyCode == 55 && mouseLeftButtonPressed) {
                    clientSideHandler.sendMouseLeftReleaseMessage();
                    mouseLeftButtonPressed = false;
                }

                if (keyCode == 57 && !mouseRightButtonPressed) {
                    clientSideHandler.sendMouseRightPressMessage();
                    mouseRightButtonPressed = true;
                } else if (keyCode == 57 && mouseRightButtonPressed) {
                    clientSideHandler.sendMouseRightReleaseMessage();
                    mouseRightButtonPressed = false;
                }
            } catch (IOException ex) {
                ex.printStackTrace();
                BTMidlet.getInstance().showAlert(ex);
            }
        }
    }

    /**
     *
     * @param graphics
     */
    protected void paint(Graphics graphics) {
        /* draws the background */
        if (desktopImage == null) {
            graphics.setColor(0, 0, 0);
            graphics.fillRect(0, 0, getWidth(), getHeight());
        } else {
            graphics.drawImage(desktopImage, 0, 0, Graphics.LEFT | Graphics.TOP);
        }

        /* draws the mouse */
        graphics.setColor(0, 0, 0);
        graphics.fillTriangle(mouseX, mouseY, mouseX + 3, mouseY + 9, mouseX + 9, mouseY + 3);
        graphics.setColor(255, 255, 255);
        graphics.drawLine(mouseX, mouseY, mouseX + 3, mouseY + 9);
        graphics.drawLine(mouseX, mouseY, mouseX + 9, mouseY + 3);
        graphics.drawLine(mouseX + 3, mouseY + 9, mouseX + 9, mouseY + 3);
    }

    /**
     *
     * @param keyCode
     */
    protected void keyPressed(int keyCode) {
        switch (keyCode) {
            case (- 1):
                padUp = true;
                break;
            case (- 2):
                padDown = true;
                break;
            case (- 3):
                padLeft = true;
                break;
            case (- 4):
                padRight = true;
                break;
        }

        try {
            updateZoom(keyCode);
            updateViewportPosition(keyCode);

            /* mouse events */
            processMouseClicks(keyCode);
            processMousePressesReleases(keyCode);

            /* keyboard input */
            if (keyCode == 53) {
//            	IDesktopClient.getInstance().showKeyboardInputTextBox(this);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            BTMidlet.getInstance().showAlert(ex);
        }
    }

    /**
     *
     * @param keyCode
     */
    protected void keyReleased(int keyCode) {
        switch (keyCode) {
            case (- 1):
                padUp = false;
                clientSideHandler.sendMoveMouseMessage();
                break;
            case (- 2):
                padDown = false;
                clientSideHandler.sendMoveMouseMessage();
                break;
            case (- 3):
                padLeft = false;
                clientSideHandler.sendMoveMouseMessage();
                break;
            case (- 4):
                padRight = false;
                clientSideHandler.sendMoveMouseMessage();
                break;
        }

        try {
            clientSideHandler.sendRequestScreenshotMessage();
        } catch (Exception ex) {
            ex.printStackTrace();
            BTMidlet.getInstance().showAlert(ex);
        }
    }

    /**
     *
     * @param command
     * @param displayable
     */
    public void commandAction(Command command, Displayable displayable) {
        if (disconnectCommand == command) {
        	//TODO: implement me
        }
    }

    /**
     *
     * @return
     */
    public Image getDesktopImage() {
        return desktopImage;
    }

    /**
     *
     * @param desktopImage
     */
    public void setDesktopImage(Image desktopImage) {
        this.desktopImage = desktopImage;
        desktopImageUpdated = true;
    }

    /**
     *
     * @return
     */
    public ClientSideHandler getClientSideHandler() {
        return clientSideHandler;
    }

    /**
     *
     * @param clientSideHandler
     */
    public void setClientSideHandler(ClientSideHandler clientSideHandler) {
        this.clientSideHandler = clientSideHandler;
    }
    
    /**
     *
     * @return
     */
    public int getZoom() {
        return zoom;
    }

    /**
     *
     * @param zoom
     */
    public void setZoom(int zoom) {
        this.zoom = zoom;
    }

    /**
     *
     * @return
     */
    public int getViewportX() {
        return viewportX;
    }

    /**
     *
     * @param viewportX
     */
    public void setViewportX(int viewportX) {
        this.viewportX = viewportX;
    }

    /**
     *
     * @return
     */
    public int getViewportY() {
        return viewportY;
    }

    /**
     *
     * @param viewportY
     */
    public void setViewportY(int viewportY) {
        this.viewportY = viewportY;
    }

    /**
     *
     * @return
     */
    public int getViewportWidth() {
        return viewportWidth;
    }

    /**
     *
     * @param viewportWidth
     */
    public void setViewportWidth(int viewportWidth) {
        this.viewportWidth = viewportWidth;
    }

    /**
     *
     * @return
     */
    public int getViewportHeight() {
        return viewportHeight;
    }

    /**
     *
     * @param viewportHeight
     */
    public void setViewportHeight(int viewportHeight) {
        this.viewportHeight = viewportHeight;
    }

    /**
     *
     * @return
     */
    public int getMouseStep() {
        return mouseStep;
    }

    /**
     *
     * @param mouseStep
     */
    public void setMouseStep(int mouseStep) {
        this.mouseStep = mouseStep;
    }

    /**
     *
     * @return
     */
    public int getMouseX() {
        return mouseX;
    }

    /**
     *
     * @param mouseX
     */
    public void setMouseX(int mouseX) {
        this.mouseX = mouseX;
    }

    /**
     *
     * @return
     */
    public int getMouseY() {
        return mouseY;
    }

    /**
     *
     * @param mouseY
     */
    public void setMouseY(int mouseY) {
        this.mouseY = mouseY;
    }

    /**
     *
     * @return
     */
    public int getRefreshInterval() {
        return refreshInterval;
    }

    /**
     *
     * @param refreshInterval
     */
    public void setRefreshInterval(int refreshInterval) {
        this.refreshInterval = refreshInterval;
    }
}
